import poser, os, sys, string, re, time, shutil, gzip

os.chdir( os.path.dirname(poser.AppLocation()) )

if float(poser.Version()) >= 7:
        import re
else:
        import pre as re

sys.path.append( os.path.join("Runtime","Python","PoseWorks","GlowWorm","Data"))

import gwrRender, gwrPass, gwrParse, gwoClip, gwoDof
from gwrVars import *

renderJobsDir = os.path.join("Runtime","Python","PoseWorks","GlowWorm","Jobs")

excCancelRender = "Job canceled by user."

VAR_TEMPSCENE, VAR_TEMPPMD = "temp.pz3", "temp.pmd"

def quickCompress(file, data):
	import gzip
	zfi = gzip.GzipFile(file,'wb')
	zfi.write(data)
	zfi.close()

def quickDecompress(file):
	import gzip
	zfi = gzip.open(file)
	r = zfi.read()
	zfi.close()
	return r

class renderJob:
	def __init__(self, srcpath="None", outdir="None", format="TIF", range=VAR_USEDOC, height=VAR_USEDOC, width=VAR_USEDOC, settings=None, passes=[]):
		self.srcpath = srcpath
		self.outdir = outdir
		self.passes = passes
		self.height = height
		self.width = width
		self.format = format
		self.range = range
		self.status = "Queued."
		if not settings:
			self.settings = gwrRender.genericRS
		else:
			self.settings = settings
		self.passes = passes
		if self.width != VAR_USEDOC:
			self.settings.setOutputRes(self.width, self.height)
	def getSource(self):
		return os.path.split(self.srcpath)[-1]
	def getOutdir(self):
		return os.path.split(self.outdir)[-1]
	def getPasses(self):
		outstr = ""
		for rpass in self.passes:
			outstr = outstr + rpass.id + " "
		return outstr
	def getStatus(self):
		return self.status
	def getFormat(self):
		return self.format
	def getFrames(self):
		if self.range == VAR_USEDOC:
			return "(Doc)"
		elif self.range == VAR_CURRENT:
			return "(Cur)"
		else:
			return "%i-%i"  % (int(self.range[0]), int(self.range[1]))
	def getRes(self):
		if self.height == VAR_USEDOC:
			return "(Doc)"
		else:
			return "%ix%i" % (int(self.width), int(self.height))

def runSimulations(scene):
	simsDone = 0
	for i in range( scene.NumClothSimulators() ):
		scene.ClothSimulator(i).CalculateDynamics()
		simsDone = simsDone + 1
	for act in scene.Actors():
		if act.NumHairGroups():
			for i in range( act.NumHairGroups() ):
				act.HairGroup(i).CalculateDynamics()
				simsDone = simsDone + 1
	return simsDone

class jobManager:
	def __init__(self, jobs, runSims=0):
		self.jobs = jobs
		self.renderTime = 0
		self.timeLeft = 0
		self.renderQueue = []
		self.status = "Waiting"
		self.percentDone = 0.0
		self.framesDone = 0
		self.abort = 0
		self.runSims = runSims
		self.current = ""
	def queueJob(self, job):
		self.jobs.append(job)
	def getNumPasses(self, scene, job):
		numPasses = len(job.passes)
		if not numPasses: return 1
		else:
			for rp in job.passes:
				if self.doMultiLightPasses(rp):
					numPasses = numPasses + scene.NumLights()-1
				if self.doMultiObjectPasses(rp):
					numPasses = numPasses + len(self.getRenderableObjects(scene)) - 1
		return numPasses
	def getRenderableObjects(self, scene):
		renderObjects = []
		for actor in scene.Actors():
			if actor.IsProp():
				if not actor.Parent().InternalName() != "UNIVERSE": pass
				elif not actor.VisibleInRender(): pass
				elif actor.IsBase(): pass
				elif actor.IsDeformer(): pass
				elif actor.IsZone(): pass
				elif re.search("CenterOf", actor.InternalName(), re.I): pass
				elif not hasattr(actor, "Materials"): pass
				else: renderObjects.append(actor)
		for figure in scene.Figures():
			if not figure.ParentActor().Parent().InternalName() != "UNIVERSE": pass
			elif not figure.Visible(): pass
			else: renderObjects.append(figure)
		return renderObjects
	def calculatePercentDone(self, scene, job):
		framesToDo = len(self.getFrameRange(scene, job.range)) * self.getNumPasses(scene, job) * len(self.renderQueue)
		if framesToDo == 0: self.percentDone = 100.0
		else: self.percentDone = float(self.framesDone) / framesToDo * 100
	def estimateTimeLeft(self, renderTime, totalFrames, framesDone, passesPerFrame):
		etl = renderTime * (totalFrames - framesDone) * passesPerFrame
	def updateStatus(self):
		return self.status
	def updateCheck(self):
		return self.status
	def finishJob(self):
		return 1
	def setStatus(self, newStatus):
		self.status = newStatus
		self.updateStatus()
	def ABORT(self):
		self.abort = 1
		self.status = "Render canceled."
		raise excCancelRender
	def checkCanGo(self):
		self.updateCheck()
		poser.Scene().ProcessSomeEvents(1)
		if self.abort: self.ABORT()
	def renderAll(self):
		self.status = "Getting ready to render..."
		self.updateStatus()
		for job in self.jobs:
			self.setStatus( self.renderJob(job) )
			self.finishJob()
		return "Done"
	def queueScene(self, sceneFile):
		self.renderQueue.append(sceneFile)
	def getOutDir(self, job):
		if not os.path.isdir(job.outdir):
			os.mkdir(job.outdir)
		return job.outdir
	def getRenderPasses(self, job):
		if job.passes == []: return [None]
		else: return job.passes
	def applyRenderSettings(self, scene, settings):
		settings.forceManual(scene)
		settings.apply(scene)
	def renderJob(self, job):
		if self.abort: return "Canceled."
		self.curJob = job
		gwrParse.saveJobAs(job, "Last.gw6")
		if os.path.isfile(job.srcpath):
			self.queueScene(job.srcpath)
		elif os.path.isdir(job.srcpath):
			for scene in os.listdir(job.srcpath):
				if scene[-3:].lower() == "pz3" or scene[-3:].lower() == "pzz":
					self.queueScene( os.path.join(job.srcpath, scene) )
				else:
					continue
		else:
			return "%s not found." % os.path.basename(job.srcpath)
		for pz3 in self.renderQueue:
			self.setStatus( "Queued %s" % pz3 )
			self.prepSceneFile(pz3)
			self.renderScene(pz3, job, self.getOutDir(job), format=job.format, frames=job.range, settings=job.settings)
		self.framesDone = 0
		self.renderQueue = []
		return "Done"
	def doMultiLightPasses(self, renderPass):
		if hasattr(renderPass, "passPerLight"):
			return renderPass.passPerLight
		else:
			return 0
	def doMultiObjectPasses(self, renderPass):
		if hasattr(renderPass, "passPerObject"):
			return renderPass.passPerObject
		else:
			return 0
	def toggleVisible(self, obj, v):
		if hasattr(obj, "SetVisible"):
			obj.SetVisible(v)
		else:
			obj.SetVisibleInRender(v)
	def renderScene(self, srcpath, job, outdir, format="TIF", frames=(0,0), settings=None):
		self.checkCanGo()
		poser.CloseDocument(1)
		poser.OpenDocument(self.current) #VAR_TEMPSCENE)
		if self.runSims:
			poser.RevertDocument()
			runSimulations(poser.Scene())
			#poser.SaveDocument(VAR_TEMPSCENE)
		self.checkCanGo()
		for renderPass in self.getRenderPasses(job):
			poser.RevertDocument()
			self.checkCanGo()
			scene = poser.Scene()
			passID = self.prepScene(scene, renderPass, settings)
			frameRange = self.getFrameRange(scene, frames)
			if self.doMultiLightPasses(renderPass):
				lightIntens = {}
				for light in scene.Lights():
					iParm = light.ParameterByCode(poser.kParmCodeKDINTENSITY)
					lightIntens[light.Name()] = iParm.Value()
			#elif self.doMultiObjectPasses(renderPass):
			#	renderObjects = self.getRenderableObjects(scene)
			clips = gwoClip.getClips(scene)
			invisibles = gwoClip.getInvisibles(scene)
			if len(clips) == 1: ignoreClips = 1
			else: ignoreClips = 0
			for nf in clips:
				if not ignoreClips:
					if passID: pid = passID + ".x%i" % clips.index(nf)
					else: pid = "x%i" % clips.index(nf)
					gwoClip.clipScene(scene, clipNear=nf[0], clipFar=nf[1], invisibles=invisibles)
				else:
					pid = passID
				for fN in frameRange:
					self.calculatePercentDone(scene, job)
					scene.SetFrame(fN)
					if hasattr(renderPass, "zbuffer"):
						if renderPass.zbuffer: gwoClip.depthCue(scene)
					gwoDof.setDof()
					if self.doMultiLightPasses(renderPass):
						for light in scene.Lights():
							self.calculatePercentDone(scene, job)
							gwrPass.singleLightScene(scene, light)
							outfileName = self.getOutFileName(srcpath, pid + "." + light.Name(), fN+1, format)
							self.doRender(scene, format, outdir, outfileName)
					#elif self.doMultiObjectPasses(renderPass):
					#	for rObj in renderObjects:
					#		self.toggleVisible(rObj, 0)
					#	for rObj in renderObjects:
					#		self.calculatePercentDone(scene, job)
					#		self.toggleVisible(rObj, 1)
					#		outfileName = self.getOutFileName(srcpath, pid + "." + rObj.Name(), fN+1, format)
					#		self.doRender(scene, format, outdir, outfileName)
					#		self.toggleVisible(rObj, 0)
					else:
						outfileName = self.getOutFileName(srcpath, pid, fN+1, format)
						self.doRender(scene, format, outdir, outfileName)
					self.calculatePercentDone(scene, job)
					self.updateStatus()
				gwoClip.restoreVisibility(scene, invisibles)
	def doRender(self, scene, format, outdir, outfileName):
		self.setStatus( "Rendering %s..." % os.path.basename(outfileName) )
		self.checkCanGo()
		scene.Render()
		self.checkCanGo()
		outfile = os.path.join(outdir, outfileName)
		scene.SaveImage(format, outfile)
		self.setStatus( "Saving %s" % outfile )
		self.framesDone = self.framesDone + 1
	def prepSceneFile(self, srcpath):
		for tmp in [VAR_TEMPSCENE, VAR_TEMPPMD]:
			if os.path.exists(tmp):
				if not os.access(tmp, os.W_OK):
					import stat
					os.chmod(tmp, stat.S_IWRITE)
				os.remove(tmp)
		self.setStatus( "Prepping %s..." % os.path.basename(srcpath) )
		if srcpath[-1] == "3":
			self.current = srcpath #shutil.copyfile(srcpath, VAR_TEMPSCENE)
		else:
			data = quickDecompress(srcpath)
			fi = open(VAR_TEMPSCENE,'w')
			fi.write(data)
			fi.close()
			pmd_path = srcpath[:-3]+"pmd"
			if os.path.exists(pmd_path):
				shutil.copyfile(pmd_path, VAR_TEMPPMD)
			self.current = VAR_TEMPSCENE
	def prepScene(self, scene, renderPass, settings):
		self.applyRenderSettings(scene, settings)
		if renderPass:
			renderPass.apply(scene)
			pID = renderPass.id
		else:
			pID = ""
		return pID
	def getFrameRange(self, scene, frames):
		if frames == VAR_USEDOC:
			mm = scene.MovieMaker()
			frames = (mm.OutputStartFrame()+1,mm.OutputEndFrame()+1)
		elif frames == VAR_CURRENT:
			return [scene.Frame()]
		frameRange = range(frames[1] - frames[0] + 1)
		startFrame = frames[0] - 1
		if frameRange == []: frameRange = [scene.Frame()]
		outFrames = []
		for f in frameRange: outFrames.append(startFrame + f)
		return outFrames
	def getOutFileName(self, srcpath, passID, frameNumber, format):
		fN = repr(frameNumber)
		while len(fN) < 3: fN = "0" + fN
		if len(passID): pID = "." + passID
		else: pID = ""
		return os.path.basename(srcpath)[:-4] + pID + "." + fN + "." + format.lower()

def newJobFileName():
	jobs = os.listdir(renderJobsDir)
	if len(jobs):
		i = int(jobs[-1][:-4]) + 1
	else:
		i = 1
	d = repr(i)
	while len(d) < 4: d = "0" + d
	return os.path.join(renderJobsDir, "%s.gw6" % d)

def createJob(jobfile=None, srcpath=None, outdir=None, format="tif", range=VAR_USEDOC, height=VAR_USEDOC, width=VAR_USEDOC, settings=None, passes=[]):
	if not jobfile:
                jobfile = newJobFileName()
	gwrParse.saveJobAs(renderJob(srcpath=srcpath, outdir=outdir, format=format, range=range, height=height, width=width, settings=settings, passes=passes), jobfile)

def validJob(jobfile):
	if os.path.isdir(jobfile): return 0
	elif jobfile[-3:] != "gw6": return 0
	else:	return 1

def getJobs():
	gw6s = []
	files = filter(validJob, os.listdir(renderJobsDir))
	for file in files: gw6s.append(os.path.join(renderJobsDir,file))
	if len(gw6s) > 16: gw6s = gw6s[:16]
	gw6s.sort()
	return gw6s

if __name__ == '__main__':
	jm = jobManager()

pass
